package monitor

import (
	"fmt"
	"github.com/astaxie/beego/orm"
	"time"
	"path/filepath"
	"os"
	"github.com/astaxie/beego/logs"
	"cloud_worm/util/proto/ujson"
	"cloud_worm/util/uhttp"
	"cloud_worm/util/umodels/mysqldb"
	"cloud_worm/util/umodels/mysqldb/dbmanager"
	"cloud_worm/util/umodels/mysqldb/dbmonitor"
	"cloud_worm/wormweb/controllers/ctrconfig"
	"cloud_worm/wormweb/controllers/ctrutil"
	"cloud_worm/wormweb/controllers/ctrinner/taskqueue"
	"cloud_worm/util/ufile"
)

type LogCollectorController struct {
	ctrconfig.LoginCtr
}

func (l *LogCollectorController) ListParser()  {
	l.Manager()

	var parser []string
	parser = append(parser, ujson.SysLogLogParser)
	parser = append(parser, ujson.MessageLogParser)
	// TODO：新增

	data := map[string]interface{}{"total": len(parser), "data": parser}
	l.Data["json"] = uhttp.CreateOkResponse(data)
	l.ServeJSON()
	return
}

func (l *LogCollectorController) List() {
	l.Manager()

	offset, limit := l.DealPage()
	status, err := l.GetInt("status", mysqldb.StatusAll)
	fileName := l.GetString("file_name")

	w := &dbmonitor.LogCollector{}
	count, err := w.Count(status, fileName)
	if err != nil {
		logs.Warn(fmt.Sprintf("LogCollector count error: %s", err.Error()))
		data := map[string]interface{}{"total": 0, "data": []dbmonitor.LogCollector{}}
		l.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, "count error.", data)
		l.ServeJSON()
		return
	}
	if count == 0 {
		data := map[string]interface{}{"total": count, "data": []dbmonitor.CmdWhite{}}
		l.Data["json"] = uhttp.CreateOkResponse(data)
		l.ServeJSON()
		return
	}

	result, err := w.Select(offset, limit, status, fileName)
	if err != nil {
		logs.Warn(fmt.Sprintf("LogCollector select error: %s", err.Error()))
		data := map[string]interface{}{"total": 0, "data": []dbmonitor.LogCollector{}}
		l.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, "select error.", data)
		l.ServeJSON()
		return
	}

	data := map[string]interface{}{"total": count, "data": result}
	l.Data["json"] = uhttp.CreateOkResponse(data)
	l.ServeJSON()
	return
}

func (l *LogCollectorController) Create()  {
	l.Manager()

	type InputInfo struct {
		FileName 		string			`json:"file_name"`
		Parser 			string			`json:"parser"`
	}
	var input InputInfo
	if err := l.Unmarshal(&input); err != nil {
		l.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeReqBodyErr, err.Error(), nil)
		l.ServeJSON()
		return
	}
	if input.FileName == mysqldb.SelectStrAll {
		l.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, "input params error.", nil)
		l.ServeJSON()
		return
	}
	if input.Parser != ujson.MessageLogParser && input.Parser != ujson.SysLogLogParser {
		l.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, "input parser error.", nil)
		l.ServeJSON()
		return
	}

	w := dbmonitor.LogCollector{FileName: input.FileName}
	if err := w.Read("file_name"); err == nil {
		logs.Error(fmt.Sprintf("had log file_name: %s", input.FileName))
		l.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, "the log file is had.", nil)
		l.ServeJSON()
		return
	}
	w.Parser = input.Parser
	w.Created = time.Now().Unix()
	w.Updated = w.Created
	w.Status = mysqldb.StatusValid
	if err := w.Insert(); err != nil {
		logs.Error(fmt.Sprintf("LogCollector insert error: %s", err.Error()))
		l.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, "insert error.", nil)
		l.ServeJSON()
		return
	}

	fd, err := os.OpenFile(logCollectorFilePath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0600)
	if err != nil {
		logs.Error(fmt.Sprintf("LogCollector create file error: %s", err.Error()))
		l.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, "create file error.", nil)
		l.ServeJSON()
		return
	}
	defer fd.Close()

	l.Data["json"] = uhttp.CreateOkResponse(nil)
	l.ServeJSON()
	return
}

func (l *LogCollectorController) Edit() {
	l.Manager()

	type InputInfo struct {
		Id 				int64			`json:"id"`
		FileName 		string			`json:"file_name"`
		Parser 			string			`json:"parser"`
		Status 			int				`json:"status"`
	}
	var input InputInfo
	if err := l.Unmarshal(&input); err != nil {
		l.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeReqBodyErr, err.Error(), nil)
		l.ServeJSON()
		return
	}
	if input.FileName == mysqldb.SelectStrAll || input.Parser == mysqldb.SelectStrAll {
		l.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, "input log collector config error.", nil)
		l.ServeJSON()
		return
	}

	// 执行修改
	w := &dbmonitor.LogCollector{Id: input.Id}
	if err := w.Read("id"); err != nil {
		logs.Warn(fmt.Sprintf("LogCollector edit input id error: %s.", err.Error()))
		l.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, "input id error.", nil)
		l.ServeJSON()
		return
	}
	w.Updated = time.Now().Unix()
	w.Status = input.Status
	w.Parser = input.Parser
	w.FileName = input.FileName
	if err := w.Update("updated", "status", "parser", "file_name"); err != nil {
		logs.Warn(fmt.Sprintf("LogCollector updated error: %s.", err.Error()))
		l.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, "updated error.", nil)
		l.ServeJSON()
		return
	}

	fd, err := os.OpenFile(logCollectorFilePath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0600)
	if err != nil {
		logs.Error(fmt.Sprintf("LogCollector create file error: %s", err.Error()))
		l.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, "create file error.", nil)
		l.ServeJSON()
		return
	}
	defer fd.Close()

	l.Data["json"] = uhttp.CreateOkResponse(nil)
	l.ServeJSON()
	return
}

// LogCollectorController.CommitFlag 标记是否需要commit处理
// log_collector_rule_file 文件存在表明是待commit的
// 因为commit时，会将log_collector_rule_file重命名为log_collector_rule_file.bak，所以一旦commit过，log_collector_rule_file就不会存在
func (l *LogCollectorController) CommitFlag() {
	l.Manager()

	commit := false
	if _, err := os.Stat(logCollectorFilePath); err != nil {
		commit = false
	} else {
		commit = true
	}

	l.Data["json"] = uhttp.CreateOkResponse(map[string]interface{}{"commit": commit})
	l.ServeJSON()
	return
}

func (l *LogCollectorController) Commit() {
	l.Manager()

	w := &dbmonitor.LogCollector{}
	ws, err := w.Select(mysqldb.SelectOffsetZero, mysqldb.SelectLimitAll, mysqldb.StatusValid, mysqldb.SelectStrAll)
	if err != nil {
		logs.Error(fmt.Sprintf("select all log collector error: %s", err.Error()))
		l.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, "select all log collector key error", nil)
		l.ServeJSON()
		return
	}
	fd, err := os.OpenFile(logCollectorFilePath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0600)
	if err != nil {
		logs.Error(fmt.Sprintf("open file'%s' error: %s", logCollectorFilePath, err.Error()))
		l.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, fmt.Sprintf("open file'%s' error",
			logCollectorFilePath), nil)
		l.ServeJSON()
		return
	}
	for _, item := range ws {
		fd.WriteString(fmt.Sprintf("%s|%s\n", item.FileName, item.Parser))
	}
	fd.Close()

	newMd5 := ufile.GetFileMd5(logCollectorFilePath)
	oldMd5 := ufile.GetFileMd5(logCollectorFileBakPath)
	if newMd5 == oldMd5 {
		os.Rename(logCollectorFilePath, logCollectorFileBakPath)
		l.Data["json"] = uhttp.CreateOkResponse(nil)
		l.ServeJSON()
		return
	}

	downloadURL, err := ctrutil.ResourceSet().UploadFile(logCollectorFilePath, ctrutil.UploadWormURL)
	if err != nil {
		logs.Error(fmt.Sprintf("upload file to router error: %s", err.Error()))
		l.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, "upload file to router error", nil)
		l.ServeJSON()
		return
	}
	os.Rename(logCollectorFilePath, logCollectorFileBakPath)

	// 如果没有启动命令监控的功能，则仅将配置文件提交
	if LogCollector == false {
		logs.Debug("log collector=%v, commit config file", LogCollector)
		l.Data["json"] = uhttp.CreateOkResponse(nil)
		l.ServeJSON()
		return
	}

	// 选择目标
	wormId := dbmanager.WormId{}
	wormIds, err := wormId.SelectStatusVerOver(mysqldb.SelectOffsetZero, mysqldb.SelectLimitAll, dbmanager.AgentOnline,
		LogCollectorConfigVersion, ctrutil.RunMode)
	if err != nil && err != orm.ErrNoRows {
		logs.Error(fmt.Sprintf("log collector get online agent failed: %s", err.Error()))
		l.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, "log collector get online agent failed.", nil)
		l.ServeJSON()
		return
	}

	confPayLoad := ujson.ConfigFilePayLoad{
		FileURL: downloadURL,
		MD5: newMd5,
	}
	payLoad, err := confPayLoad.MarshalJSON()
	if err != nil {
		logs.Error(fmt.Sprintf("log collector ConfigFilePayLoad marshal json failed: %s", err.Error()))
		l.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, "log collector ConfigFilePayLoad marshal json failed.", nil)
		l.ServeJSON()
		return
	}
	for _, item := range wormIds {
		notifyItem := &NotifyAutoConfig{
			AccessSrv: item.ServerAddr,
			AgentID: item.AgentId,
			AgentIP: item.IP,
			NotifyType: ujson.NtLogCollectorRule,
			PayLoad: payLoad,
		}
		taskqueue.FastQueInst().MsgQ.Push(notifyItem)
	}

	l.Data["json"] = uhttp.CreateOkResponse(nil)
	l.ServeJSON()
	return
}

func (l *LogCollectorController) EnableSet()  {
	l.Manager()

	type InputInfo struct {
		Enable 		bool		`json:"enable"`
	}
	var input InputInfo
	if err := l.Unmarshal(&input); err != nil {
		l.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeReqBodyErr, err.Error(), nil)
		l.ServeJSON()
		return
	}
	if LogCollector == input.Enable {
		logs.Debug("log collector enable had:", LogCollector)
		l.Data["json"] = uhttp.CreateOkResponse(nil)
		l.ServeJSON()
		return
	}

	LogCollector = input.Enable
	logCollectorUpdate()

	// 选择目标
	wormId := dbmanager.WormId{}
	wormIds, err := wormId.SelectStatusVerOver(mysqldb.SelectOffsetZero, mysqldb.SelectLimitAll, dbmanager.AgentOnline,
		LogCollectorConfigVersion, ctrutil.RunMode)
	if err != nil && err != orm.ErrNoRows {
		logs.Error(fmt.Sprintf("log collector enable get online agent failed: %s", err.Error()))
		l.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, "log collector enable get online agent failed.", nil)
		l.ServeJSON()
		return
	}

	// 转发出去
	enablePayLoad := ujson.EnablePayLoad{
		Enable: LogCollector,
	}
	payLoad, err := enablePayLoad.MarshalJSON()
	if err != nil {
		logs.Error(fmt.Sprintf("log collector EnablePayLoad marshal json failed: %s", err.Error()))
		l.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, "EnablePayLoad marshal json failed.", nil)
		l.ServeJSON()
		return
	}
	for _, item := range wormIds {
		notifyItem := &NotifyAutoConfig{
			AccessSrv: item.ServerAddr,
			AgentID: item.AgentId,
			AgentIP: item.IP,
			NotifyType: ujson.NtLogCollector,
			PayLoad: payLoad,
		}
		taskqueue.FastQueInst().MsgQ.Push(notifyItem)
	}

	l.Data["json"] = uhttp.CreateOkResponse(nil)
	l.ServeJSON()
	return
}

func (l *LogCollectorController) EnableGet()  {
	l.Manager()

	l.Data["json"] = uhttp.CreateOkResponse(map[string]interface{}{"enable": LogCollector})
	l.ServeJSON()
	return
}

func logCollectorUpdate() {
	conf := &dbmanager.WebConfig{Name: LogCollectorEnableName}
	if err := conf.Read("name"); err == nil {
		conf.Value = fmt.Sprintf("%v", LogCollector)
		conf.Updated = time.Now().Unix()
		conf.Update("value", "updated")
	} else {
		logs.Error(fmt.Sprintf("no '%s' config record", LogCollectorEnableName))
	}
	return
}

var LogCollector bool

const LogCollectorEnableName = "log_collector_enable"

func LogCollectorLoad() error {
	conf := &dbmanager.WebConfig{Name: LogCollectorEnableName}
	if err := conf.Read("name"); err == nil {
		switch conf.Value {
		case "1", "t", "T", "true", "TRUE", "True", "YES", "yes", "Yes", "Y", "y", "ON", "on", "On":
			LogCollector = true
		case "0", "f", "F", "false", "FALSE", "False", "NO", "no", "No", "N", "n", "OFF", "off", "Off":
			LogCollector = false
		default:
			return fmt.Errorf("config value format error")
		}
	} else {
		return fmt.Errorf("读WebConfig(%s)错误: %s", LogCollectorEnableName, err.Error())
	}

	logs.Info(fmt.Sprintf("set %s=%v", LogCollectorEnableName, LogCollector))
	return nil
}

func init()  {
	ctrconfig.RegisterConfigLoad(LogCollectorEnableName, LogCollectorLoad)

	// 尝试创建该列数据
	conf := &dbmanager.WebConfig{Name: LogCollectorEnableName}
	if err := conf.Read("name"); err != nil {
		conf.Commit = "log collector开关"
		conf.Type = dbmanager.BoolValue
		conf.Value = "false"
		conf.Created = time.Now().Unix()
		conf.Updated = conf.Created
		conf.Status = mysqldb.StatusValid
		conf.Insert()
	}

	LogCollectorLoad()
}

var logCollectorFilePath string
var logCollectorFileBakPath string
func init()  {
	logCollectorFilePath = filepath.Join(ctrutil.TmpDir, ujson.LogCollectorRuleFile)
	logCollectorFileBakPath = filepath.Join(ctrutil.TmpDir, ujson.LogCollectorRuleFile + ".bak")
}